<?php
/**
 * Created by PhpStorm.
 * User: DongYao
 * Date: 2017/12/21
 * Time: 下午9:23
 */

namespace App\Support\Order;

use App\Events\WebAppOrderChange;
use App\Jobs\BaiduPushOrderEndedMsg;
use App\Jobs\WechatPushOrderEndedMsg;
use App\Tables\AgentBalanceTransferRecord;
use App\Tables\AgentScoreTransferRecord;
use App\Tables\Device;
use App\Tables\OrderPayment;
use App\Tables\User;
use App\Tables\UserBalanceTransferRecord;
use App\Tables\UserScoreTransferRecord;
use Carbon\Carbon;
use DB;
use Exception;
use Throwable;
use Illuminate\Database\QueryException;
use App\Tables\Order as OrderTable;
use Log;
use App\Events\DeviceStatusChange;

class Order
{
    /**
     * 生成订单
     * @param $user_id
     * @param $device_sn
     * @param $port
     * @param string $source
     * @return OrderTable|bool
     */
    public function create($user_id, $device_sn, $port, $source = 'web')
    {
        DB::beginTransaction();
        try {
            $user = User::where('id', $user_id)->first();
            if (empty($user)) {
                throw new Exception('下单用户信息不存在');
            }

            $device = Device::where('device_sn', $device_sn)->sharedLock()->first();
            if (empty($device)) {
                throw new Exception('设备信息不存在');
            }

            if ($device->status != 'online') {
                throw new Exception('设备状态不在线，不能下单');
            }

            if ($device->ports[$port] == 1) {
                throw new ApiException('充电端口正在使用中');
            }

            if ($user->balance < $device->ladder_price['four']['price'] * config('system.superpower_multiple', 1)) {
                throw new Exception('账户余额不足，请充值后再使用');
            }

            $ports = $device->ports;
            $ports[$port] = 1;

            $device->ports = $ports;
            $res = $device->save();
            if ($res === false) {
                throw new Exception('设备端口锁定失败');
            }

            /**
             * 生成订单
             */
            $order = new OrderTable();
            $order->user_id = $user_id;
            $order->agent_user_id = $device->user_id;
            $order->order_sn = $order->makeOrderSn();
            $order->device_sn = $device->device_sn;
            $order->order_money = 0;
            $order->real_pay_money = 0;
            $order->status = 'wait_pay';
            //$order->service_start_at = Carbon::now();
            $order->port = $port;
            $order->proration = $device->agent->extension->proration;
            $order->prorations = $device->agent->extension->prorations;
            $order->ladder_price = $device->ladder_price;
            $order->source = $source;
            $res = $order->save();
            if ($res === false) {
                throw new Exception('生成订单失败');
            }
            DB::commit();

            // 通知前端页面更改状态
            event(new DeviceStatusChange($device));

            return $order;
        } catch (Exception $exception) {
            Log::error('下单失败', [$exception->getMessage()]);
            DB::rollBack();
        } catch (QueryException $e) {
            Log::error('下单失败', [$e->getMessage(), $e->getFile(), $e->getLine()]);
            DB::rollBack();
        } catch (Throwable $e) {
            Log::error('下单失败', [$e->getMessage(), $e->getFile(), $e->getLine()]);
            DB::rollBack();
        }

        return false;
    }

    /**
     * 结束订单
     * @param $order_sn
     * @param bool $superpower
     */
    public function orderEnded($order_sn, $superpower = false)
    {
        DB::beginTransAction();

        try {
            // 查询订单信息
            $order = OrderTable::where('order_sn', $order_sn)->sharedLock()->first();
            if (empty($order)) {
                throw new Exception("订单: {$order_sn} 不存在");
            }

            if ($order->status != 'service') {
                throw new Exception("订单: {$order_sn} 已经还未开始");
            }

            if ($order->status == 'success') {
                throw new Exception("订单: {$order_sn} 已经结束过了");
            }

            // 大功率充电计费倍数
            $superpower_multiple = config('system.superpower_multiple', 1);

            // 查询下单用户
            $user = User::where('id', $order->user_id)->lockForUpdate()->first();

            // 定价阶梯
            $ladder_price = $order->ladder_price;

            // 积分兑换比例
            $one_money_to_score = config('system.ladder_score.one_money_to_score', 1);
            if ($one_money_to_score <= 0) {
                $one_money_to_score = 1;
            }

            /**
             * 计算消费时间对应的价格
             */
            foreach ($ladder_price as $price_level => $_price_info) {
                if ($price_level == 'four' && $order->consume_duration >= $_price_info['min']) {
                    $order->order_money = $_price_info['price'];
                    break;
                } else if ($order->consume_duration >= $_price_info['min'] && $order->consume_duration < $_price_info['max']) {
                    $order->order_money = $_price_info['price'];
                    break;
                }
            }

            /**
             * 判断是否大功率增加收费标准
             */
            if ($superpower) {
                $old_money = $order->order_money;
                $order->order_money = bcmul($old_money, $superpower_multiple, 2);
                $order->remark = "大功率充电，结算费用： {$order->order_money} = {$old_money} * {$superpower_multiple}";
            }

            /**
             * 判断是否可以进行积分抵扣充电费用
             */
            $has_use_score = false; // 是否可以使用积分支付
            $order_score = 0; // 实际应支付积分数量
            if (config('system.ladder_score.status', false) === true) {
                // 获取支付配置信息, 验证是否足够积分支付订单
                $order_score = bcmul($order->order_money, $one_money_to_score, 0);

                // 判断用户积分账户余额是否可以扣除
                if ($user->score >= $order_score) {
                    $has_use_score = true;
                }
            }

            /**
             * 计算各级代理分红比例
             * 平台分红， 1，2，3级代理分红
             *
             * 三级代理的分红
             */
            $agent_v3 = User::where('id', $order->agent->id)->sharedLock()->first();
            if (empty($agent_v3)) {
                throw new Exception("订单: [{$order_sn}] 的三级代理不存在");
            }

            $agent_v2 = User::where('id', $agent_v3->extension->parent->id)->sharedLock()->first();
            if (empty($agent_v2)) {
                throw new Exception("订单: [{$order_sn}] 的二级代理不存在");
            }

            $agent_v1 = User::where('id', $agent_v2->extension->parent->id)->sharedLock()->first();
            if (empty($agent_v1)) {
                throw new Exception("订单: [{$order_sn}] 的一级代理不存在");
            }

            Log::debug("各级代理分成比例：", ['订单编号' => $order->order_sn, '一级' => $agent_v1->proration, '二级' => $agent_v2->proration, '三级' => $agent_v3->proration]);

            /**
             * 计算各级代理分成比例
             */
            $platform_ratio = bcsub(100, $agent_v1->proration, 3); // 平台分成
            $agent_v1_ratio = bcmul($agent_v1->proration, bcdiv(bcsub(100, $agent_v2->proration, 3), 100, 3), 3) ; // 一级代理分成比例
            $agent_v2_ratio = bcmul(bcsub($agent_v1->proration, $agent_v1_ratio, 3), bcdiv(bcsub(100, $agent_v3->proration, 3), 100, 3), 3) ; // 二级代理分成比例
            $agent_v3_ratio = bcmul(bcsub($agent_v1->proration, $agent_v1_ratio, 3), bcdiv($agent_v3->proration, 100, 3), 3) ; // 三级代理分成比例

            /**
             * 保存历史分成比例记录
             */
            $order->prorations = [
                'v0' => $platform_ratio,
                'v1' => $agent_v1_ratio,
                'v2' => $agent_v2_ratio,
                'v3' => $agent_v3_ratio,
            ];

            /**
             * 扣费逻辑
             * 优先使用积分扣除
             * 判断是否可以使用积分扣除，如果积分不够，使用余额扣费
             */
            if ($has_use_score === true) { // 积分支付
                // 实际支付金额为0
                $order->status = 'success';
                $order->real_pay_money = 0;
                $order->paid_at = Carbon::now();
                if (!$order->save()) {
                    throw new Exception("订单: [{$order_sn}] 信息保存失败");
                }

                /**
                 * 增加支付单
                 */
                $order_payment = new OrderPayment();
                $order_payment->payment_sn = $order_payment->makePaymentSn();
                $order_payment->order_sn = $order->order_sn;
                $order_payment->out_trade_sn = $order->order_sn;
                $order_payment->pay_mode = 'score';
                $order_payment->status = 'paid';
                $order_payment->money = $order_score;
                $order_payment->paid_at = Carbon::now();
                if (!$order_payment->save()) {
                    throw new Exception("订单: [{$order_sn}] 增加支付单失败");
                }

                /**
                 * 扣除下单用户积分
                 */
                $user->score = bcsub($user->score, $order_payment->money, 0);
                if (!$user->save()) {
                    throw new Exception('扣除用户积分账户余额');
                }

                // 保存下单用户积分余额变动
                $user_score_record = new UserScoreTransferRecord();
                $user_score_record->user_id = $user->id;
                $user_score_record->order_sn = $order->order_sn;
                $user_score_record->type = 'consume';
                $user_score_record->score = $order_payment->money;
                $user_score_record->total_score = $user->score;
                $res = $user_score_record->save();
                if ($res === false) {
                    throw new Exception('保存用户积分账户变动记录');
                }

                /**
                 * 计算各代理积分收益
                 */
                $agent_v1_income_score = bcmul($order_score, bcdiv($agent_v1_ratio, 100, 3), 3);
                $agent_v2_income_score = bcmul($order_score, bcdiv($agent_v2_ratio, 100, 3), 3);
                $agent_v3_income_score = bcmul($order_score, bcdiv($agent_v3_ratio, 100, 3), 3);

                /**
                 * 增加代理账户积分，并且增加积分账户变动流水记录
                 */
                $agent_v3->score = bcadd($agent_v3->score, $agent_v3_income_score, 3);
                $res = $agent_v3->save();
                if ($res === false) {
                    throw new Exception('保存三级代理分红失败');
                }

                // 保存代理账户余额变动
                $agent_v3_score_record = new AgentScoreTransferRecord();
                $agent_v3_score_record->agent_user_id = $agent_v3->id;
                $agent_v3_score_record->order_sn = $order->order_sn;
                $agent_v3_score_record->type = 'order_settlement';
                $agent_v3_score_record->num = $agent_v3_income_score;
                $agent_v3_score_record->score = $agent_v3->score;
                $res = $agent_v3_score_record->save();
                if ($res === false) {
                    throw new Exception('保存三级代理账户余额资金变动记录失败');
                }

                /**
                 * 二级代理分红
                 * 修改代理余额
                 */
                $agent_v2->score = bcadd($agent_v2->score, $agent_v2_income_score, 3);
                $res = $agent_v2->save();
                if ($res === false) {
                    throw new Exception('保存二级代理分红失败');
                }

                // 保存代理账户余额变动
                $agent_v2_score_record = new AgentScoreTransferRecord();
                $agent_v2_score_record->agent_user_id = $agent_v2->id;
                $agent_v2_score_record->order_sn = $order->order_sn;
                $agent_v2_score_record->type = 'order_settlement';
                $agent_v2_score_record->num = $agent_v2_income_score;
                $agent_v2_score_record->score = $agent_v2->score;
                $res = $agent_v2_score_record->save();
                if ($res === false) {
                    throw new Exception('保存二级代理账户余额资金变动记录失败');
                }

                /**
                 * 一级代理分红
                 * 修改代理余额
                 */
                $agent_v1->score = bcadd($agent_v1->score, $agent_v1_income_score, 3);
                $res = $agent_v1->save();
                if ($res === false) {
                    throw new Exception('保存一级代理分红失败');
                }

                // 保存代理账户余额变动
                $agent_v1_score_record = new AgentScoreTransferRecord();
                $agent_v1_score_record->agent_user_id = $agent_v1->id;
                $agent_v1_score_record->order_sn = $order->order_sn;
                $agent_v1_score_record->type = 'order_settlement';
                $agent_v1_score_record->num = $agent_v1_income_score;
                $agent_v1_score_record->score = $agent_v1->score;
                $res = $agent_v1_score_record->save();
                if ($res === false) {
                    throw new Exception('保存一级代理积分余额资金变动记录失败');
                }
            } else { // 账户余额支付；
                //交易成功
                $order->status = 'success';
                $order->real_pay_money = $order->order_money;
                $order->paid_at = Carbon::now();
                if (!$order->save()) {
                    throw new Exception("订单: [{$order_sn}] 信息保存失败");
                }

                /**
                 * 增加支付单
                 */
                $order_payment = new OrderPayment();
                $order_payment->payment_sn = $order_payment->makePaymentSn();
                $order_payment->order_sn = $order->order_sn;
                $order_payment->out_trade_sn = $order->order_sn;
                $order_payment->pay_mode = 'balance';
                $order_payment->status = 'paid';
                $order_payment->money = $order->real_pay_money;
                $order_payment->paid_at = Carbon::now();
                if (!$order_payment->save()) {
                    throw new Exception("订单: [{$order_sn}] 增加支付单失败");
                }

                /**
                 * 计算各代理收益金额
                 */
                $agent_v1_income_money = bcmul($order->real_pay_money, bcdiv($agent_v1_ratio, 100, 3), 3);
                $agent_v2_income_money = bcmul($order->real_pay_money, bcdiv($agent_v2_ratio, 100, 3), 3);
                $agent_v3_income_money = bcmul($order->real_pay_money, bcdiv($agent_v3_ratio, 100, 3), 3);

                /**
                 * 增加代理账户余额，并且增加账户资金变动流水记录
                 */
                $agent_v3->balance = bcadd($agent_v3->balance, $agent_v3_income_money, 3);
                $res = $agent_v3->save();
                if ($res === false) {
                    throw new Exception('保存三级代理分红失败');
                }

                // 保存代理账户余额变动
                $agent_v3_balance_record = new AgentBalanceTransferRecord();
                $agent_v3_balance_record->agent_user_id = $agent_v3->id;
                $agent_v3_balance_record->order_sn = $order->order_sn;
                $agent_v3_balance_record->type = 'order_settlement';
                $agent_v3_balance_record->money = $agent_v3_income_money;
                $agent_v3_balance_record->balance = $agent_v3->balance;
                $res = $agent_v3_balance_record->save();
                if ($res === false) {
                    throw new Exception('保存三级代理账户余额资金变动记录失败');
                }

                /**
                 * 二级代理分红
                 * 修改代理余额
                 */
                $agent_v2->balance = bcadd($agent_v2->balance, $agent_v2_income_money, 3);
                $res = $agent_v2->save();
                if ($res === false) {
                    throw new Exception('保存二级代理分红失败');
                }

                // 保存代理账户余额变动
                $agent_v2_balance_record = new AgentBalanceTransferRecord();
                $agent_v2_balance_record->agent_user_id = $agent_v2->id;
                $agent_v2_balance_record->order_sn = $order->order_sn;
                $agent_v2_balance_record->type = 'order_settlement';
                $agent_v2_balance_record->money = $agent_v2_income_money;
                $agent_v2_balance_record->balance = $agent_v2->balance;
                $res = $agent_v2_balance_record->save();
                if ($res === false) {
                    throw new Exception('保存二级代理账户余额资金变动记录失败');
                }

                /**
                 * 一级代理分红
                 * 修改代理余额
                 */
                $agent_v1->balance = bcadd($agent_v1->balance, $agent_v1_income_money, 3);
                $res = $agent_v1->save();
                if ($res === false) {
                    throw new Exception('保存一级代理分红失败');
                }

                // 保存代理账户余额变动
                $agent_v1_balance_record = new AgentBalanceTransferRecord();
                $agent_v1_balance_record->agent_user_id = $agent_v1->id;
                $agent_v1_balance_record->order_sn = $order->order_sn;
                $agent_v1_balance_record->type = 'order_settlement';
                $agent_v1_balance_record->money = $agent_v1_income_money;
                $agent_v1_balance_record->balance = $agent_v1->balance;
                $res = $agent_v1_balance_record->save();
                if ($res === false) {
                    throw new Exception('保存一级代理账户余额资金变动记录失败');
                }

                /**
                 * 扣除下单用户余额
                 */
                $user->balance = bcsub($user->balance, $order->order_money, 3);
                if (!$user->save()) {
                    throw new Exception('扣除用户账户余额');
                }

                // 保存下单用户账户余额变动
                $user_balance_record = new UserBalanceTransferRecord();
                $user_balance_record->user_id = $user->id;
                $user_balance_record->order_sn = $order->order_sn;
                $user_balance_record->type = 'consume';
                $user_balance_record->money = $order->order_money;
                $user_balance_record->balance = $user->balance;
                $res = $user_balance_record->save();
                if ($res === false) {
                    throw new Exception('保存用户账户余额资金变动记录失败');
                }
            }

            DB::commit();

            // 发送百度消息
            Log::debug('准备推送百度消息', [$order->order_sn, $order->device_sn]);
            dispatch(new BaiduPushOrderEndedMsg($order))->onQueue('push');

            // 推送微信公众号消息
            Log::debug('准备推送微信公众号消息', [$order->order_sn, $order->device_sn]);
            dispatch(new WechatPushOrderEndedMsg($order))->onQueue('push');

            // 推送订单状态变化给前端
            Log::debug('推送订单状态变化给前端', [$order->order_sn, $order->device_sn]);
            event(new WebAppOrderChange($order));
        } catch (Exception $exception) {
            DB::rollBack();
            Log::error("订单结束异常[{$order_sn}]", [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
        } catch (QueryException $exception) {
            DB::rollBack();
            Log::error("订单结束异常[{$order_sn}]", [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
        } catch (Throwable $exception) {
            DB::rollBack();
            Log::error("订单结束异常[{$order_sn}]", [$exception->getMessage(), $exception->getFile(), $exception->getLine()]);
        }
    }
}